import { useGLTF } from "@react-three/drei";
import { useThree } from "@react-three/fiber";
import { useEffect } from "react";
import { AnimationMixer, AxesHelper, Clock, Color, Euler, Group, Matrix4, Mesh, MeshBasicMaterial, PlaneGeometry, Quaternion, SphereGeometry, Vector3 } from "three";

import { Joint } from "./Joint";

const map = {
    nose: null,
    left_eye_inner: null,
    left_eye: null,
    mixamorigLeftEye: null,
    left_eye_outer: null,
    right_eye_inner: null,
    right_eye: null,
    right_eye_outer: null,
    left_ear: null,
    right_ear: null,
    mouth_left: null,
    mouth_right: null,
    left_shoulder: "mixamorigLeftArm",
    left_elbow: "mixamorigLeftForeArm",
    left_wrist: "mixamorigLeftHand",
    right_shoulder: "mixamorigRightArm",
    right_elbow: "mixamorigRightForeArm",
    right_wrist: "mixamorigRightHand",
    left_pinky: null,
    right_pinky: null,
    left_index: null,
    right_index: null,
    left_thumb: null,
    right_thumb: null,
    left_hip: "mixamorigLeftUpLeg",
    left_knee: "mixamorigLeftLeg",
    left_ankle: "mixamorigLeftFoot",
    right_hip: "mixamorigRightUpLeg",
    right_knee: "mixamorigRightLeg",
    right_ankle: "mixamorigRightFoot",
    left_heel: "mixamorigLeftToeBase",
    right_heel: "mixamorigRightToeBase",
    left_foot_index: null,
    right_foot_index: null,
};

const mt4 = new Matrix4();

const mt4RotateZ = new Matrix4();
mt4RotateZ.makeRotationZ(Math.PI);

const mt4RotateY = new Matrix4();
mt4RotateY.makeRotationY(Math.PI / 2);

mt4.multiply(mt4RotateZ);
mt4.multiply(mt4RotateY);

export default function Scene() {
    const { scene, animations } = useGLTF("/Xbot.glb"); // 替换为你的模型路径

    const { gl, scene: root } = useThree();

    useEffect(() => {
        root.background = new Color(0, 0, 0);
        scene.children[0].scale.set(1, 1, 1);

        scene.add(new AxesHelper(500));

        const mixer = new AnimationMixer(scene);

        const group = new Group();

        group.add(new Mesh(new SphereGeometry(5, 32, 32), new MeshBasicMaterial({ color: 0x0000ff, depthTest: false })));
        group.renderOrder = 9999;

        group.position.set(0, 103.911, 2.076);

        scene.add(group);

        const leftLeg = ["left_hip", "left_knee", "left_ankle", "left_heel"];

        const leftLegJoint = new Joint(
            scene,
            leftLeg.map((v) => [map[v]!, v])
        );

        const rightLeg = ["right_hip", "right_knee", "right_ankle", "right_heel"];

        const rightLegJoint = new Joint(
            scene,
            rightLeg.map((v) => [map[v], v])
        );

        const angle = (Math.PI / 180) * 60;

        const baseAxies = new Vector3(1, 0, 0);

        const leftArm = ["left_shoulder", "left_elbow", "left_wrist"];
        const leftArmJoint = new Joint(
            scene,
            leftArm.map((v) => [map[v]!, v]),
            new Quaternion().setFromAxisAngle(baseAxies, -angle)
        );

        const rightArm = ["right_shoulder", "right_elbow", "right_wrist"];
        const rightArmJoint = new Joint(
            scene,
            rightArm.map((v) => [map[v]!, v]),
            new Quaternion().setFromAxisAngle(baseAxies, -angle)
        );

        scene.getObjectByName("mixamorigSpine")!.rotation.x = angle;

        function updatePose(data: Array<{ name: string; score: number; x: number; y: number; z: number }>) {
            {
                const state: Record<string, { position: Vector3; wight: number }> = {};
                leftLeg.forEach((v) => {
                    const target = data.find((v2) => v2.name === v);
                    if (target) {
                        state[v] = {
                            position: new Vector3(target.x, target.y, target.z),
                            wight: target.score,
                        };
                    }
                });

                leftLegJoint.update(state);
            }

            {
                const state: Record<string, { position: Vector3; wight: number }> = {};
                rightLeg.forEach((v) => {
                    const target = data.find((v2) => v2.name === v);
                    if (target) {
                        state[v] = {
                            position: new Vector3(target.x, target.y, target.z),
                            wight: target.score,
                        };
                    }
                });

                rightLegJoint.update(state);
            }

            {
                const state: Record<string, { position: Vector3; wight: number }> = {};
                leftArm.forEach((v) => {
                    const target = data.find((v2) => v2.name === v);
                    if (target) {
                        state[v] = {
                            position: new Vector3(target.x, target.y, target.z),
                            wight: target.score,
                        };
                    }
                });

                leftArmJoint.update(state);
            }

            {
                const state: Record<string, { position: Vector3; wight: number }> = {};
                rightArm.forEach((v) => {
                    const target = data.find((v2) => v2.name === v);
                    if (target) {
                        state[v] = {
                            position: new Vector3(target.x, target.y, target.z),
                            wight: target.score,
                        };
                    }
                });

                rightArmJoint.update(state);
            }
        }

        async function loadMetaData() {
            const metaData = await fetch("/dat.json").then((res) => res.json());
            console.log(metaData);

            let index = 0;

            let timer: number;
            const loop = () => {
                timer = window.setTimeout(loop, 16);
                if (index < metaData.length) {
                    updatePose(
                        metaData[index].map((v) => {
                            const v3 = new Vector3(v.x * 100, v.y * 100, v.z * 100);
                            v3.applyMatrix4(mt4);

                            const isLeft = v.name.toLowerCase().indexOf("left") !== -1;
                            const isRight = v.name.toLowerCase().indexOf("right") !== -1;

                            return {
                                ...v,
                                x: isLeft ? 0.8 : isRight ? -0.8 : v3.x,
                                y: v3.y,
                                z: v3.z,
                            };
                        })
                    );
                } else {
                    window.clearTimeout(timer);
                }

                index++;
            };
            timer = window.setTimeout(loop, 1000 / 60);
        }

        loadMetaData();

        const clock = new Clock();

        const tick = () => {
            mixer.update(clock.getDelta());
        };

        gl.setAnimationLoop(tick);

        return () => {
            gl.setAnimationLoop(tick);
        };
    }, [animations, scene]);

    return (
        <group>
            <primitive object={scene} />
            <mesh visible={false}>
                <boxGeometry />
                <meshStandardMaterial color="blue" />
            </mesh>
        </group>
    );
}
